#ifndef parser_h__
#define parser_h__

#include <string>
#include <map>
#include <algorithm>
#include <iostream>
#include <list>

#include "tokenizer.h"
#include "basicparser.h"
#include "symbolstable.h"
#include "utills.h"

namespace CCompiler
{
    typedef std::pair<SymbolType , SymbolType> BinOpTypePair;
    typedef std::pair<std::string, BinOpTypePair> BinOpOnTypes;
    typedef SymType* (*BinOpHandler)(SynExpression*&, SynExpression*&, SymTableInterface &);
    extern std::map<BinOpOnTypes, BinOpHandler> AvailableBinOpsAndHandlers;

    typedef std::pair<std::string, SymbolType> UnaryOpOnType;
    typedef SymType* (*UnaryOpHandler)(SynExpression*&, SymTableInterface &);
    extern std::map<UnaryOpOnType, UnaryOpHandler> AvailableUnaryOpsAndHandlers;


    SymType *getExistingTypeOrInsert(SymType *type, SymTableInterface& tbl);

	class ParserWithSymTable: public BasicParser
	{
        SymFunction *currentf_;

		SymTable globalTable_; // global table
        SymTableStack stack;

        std::vector<SynStatement *> stmtContainers;
        SynStatement *defaultAllower() 
        {
            for(std::vector<SynStatement *>::reverse_iterator i = stmtContainers.rbegin();
                i != stmtContainers.rend();
                ++i)
            if((*i)->allowDefault()) return (*i);
            return NULL;
        }
        SynStatement *breakAllower() 
        {
            for(std::vector<SynStatement *>::reverse_iterator i = stmtContainers.rbegin();
                i != stmtContainers.rend();
                ++i)
                if((*i)->allowBreak()) return (*i);
            return NULL;
        }
        SynStatement *continueAllower() 
        {
            for(std::vector<SynStatement *>::reverse_iterator i = stmtContainers.rbegin();
                i != stmtContainers.rend();
                ++i)
                if((*i)->allowContinue()) return (*i);
            return NULL;
        }

        typedef std::pair<SymType*, std::string> TypeAndName;
		typedef std::vector<TypeAndName> TypesAndNames;

        enum getNamesAndTypesMode { gntmOnlyTypes, gntmFunctionLike, gntmVariables };
        void getNamesAndTypes(TypesAndNames &lst, getNamesAndTypesMode mode);
        TypeAndName getSingleNameAndType(SymType *baseType, getNamesAndTypesMode mode);

        std::string getName(getNamesAndTypesMode mode);

        void parseVariablesOrFunctionDefinition(SymTableInterface &tbl, bool disableFunctions = false);
        void parseStructures(SymTableInterface &tbl);
        void parseTypedefs(SymTableInterface &tbl);

        void expectedTokenType(TokenType type, std::string errorMsg);

        // Statements
        SynStatementBlock *parseStatementBlock();
        SynExpression *parseCondition();
        SynStatementIf *parseStatementIf();
        SynStatement *parseStatement();
        SynStatementExpression *parseStatementExpression();
        SynStatementSwitch *parseStatementSwitch();
        SynStatementBreak *parseStatementBreak();
        SynStatementConinue *parseStatementContinue();
        SynStatementDefault *parseStatementDefault();
        SynStatementCase *parseStatementCase();
        SynStatementReturn *parseStatementReturn();
        SynStatementWhile *parseStatementWhile();
        SynStatementDoWhile *parseStatementDoWhile();
        SynStatementFor *parseStatementFor();
        SynStatementPrintf *parseStatementPrintf();
        
        SymTypeStruct *extractTypeStruct(SymTableInterface &tbl);
        void getVariables(TypesAndNames &lst, SymType* baseType);

        template <class T> void insertIntoTable(SymTableInterface &tbl, TypesAndNames &varLst)
        {
            if(getTokenType() != ttSemicolon) throw Errors::SyntaxError("Missing semicolon", tok_);
            for(size_t i = 0; i < varLst.size(); ++i)
            {
                if(tbl.getClosestTable().exists(varLst[i].second)) 
                    throw Errors::SyntaxError("Name redefinition", tok_);
                if(tbl.exists(varLst[i].second) && tbl.find(varLst[i].second)->isType())
                    throw Errors::SyntaxError("Can't redefine a type", tok_);
                tbl.insert(new T(varLst[i].first, varLst[i].second));
            }
            nextToken();
        }

        bool isExistingType(Token &tok);
		bool isExistingName(Token &tok);

        SynExpression *parseFunction(SynExpression *expr);
        SynExpression *parseArray(SynExpression *expr);
        SynExpression *parseStruct(SynExpression *expr);
        SynExpression *parsePointerOnStruct(SynExpression *expr);

        virtual SynExpression *internalParseExpression(Int2Type<lowest_level>);
        SynExpression *parseUnaryExpression();
        SynExpression *parsePostfixExpression();
        SynExpression *parsePrimaryExpression();
        SynExpression *parseAssignmentExpression();

        void parseDeclaration(bool disableFunctions = false);
	public:
		virtual SynExpression *parseIdentifier();
        ParserWithSymTable(Tokenizer &tokenizer): 
            BasicParser(tokenizer), stack(&globalTable_)
        {
            globalTable_.insert(new SymTypeInt());
            globalTable_.insert(new SymTypeFloat());
            globalTable_.insert(new SymTypeVoid());
            globalTable_.insert(new SymTypeChar());
            globalTable_.insert(new SymTypePointer((SymType *) globalTable_.find("char"), "char*"));
        }
        SymTable &accessSymTable() { return globalTable_; }
		void print(std::ostream &stream)
		{
			globalTable_.print(stream);
		}
		void parse();
        SynNode *oldparse();
        virtual SynExpression *parseExpression();
	};
}

namespace __InitializeParserWithSymTable { void initialize(); }


#endif // parser_h__
